#include <string>
#include <vector>
#include <stdlib.h>
//#include "simple_log.h"
#include "redis.h"
#include <iostream>
using namespace std;

Redis::Redis()
{
}

Redis::~Redis()
{
    release();
}

void Redis::release()
{
    vector<RedisConnConf>::iterator itConn;
    for (itConn = m_conns.begin(); itConn != m_conns.end(); ++itConn)
    {
        if (itConn->m_conn != NULL)
        {
            redisFree(itConn->m_conn);
            itConn->m_conn = NULL;
        }
    }
    m_conns.clear();
}

void Redis::AddServer(const string &ip, unsigned int port,const string& passwd )
{
    m_conns.push_back(RedisConnConf(ip,port,passwd));
}

bool Redis::Init()
{
    if ( m_conns.empty() )
        return false;

    vector<RedisConnConf>::iterator itConn;
    for (itConn = m_conns.begin(); itConn != m_conns.end(); ++itConn)
    {
        if (itConn->m_conn == NULL)
        {
            itConn->m_conn = redisConnect(itConn->m_ip.c_str(), itConn->m_port);
            if ( !CheckConnStatus(*itConn) )
                return false;
        }
        else if (itConn->m_conn->err != 0)
        {
            redisFree(itConn->m_conn);
            itConn->m_conn = redisConnect(itConn->m_ip.c_str(), itConn->m_port);
            if ( !CheckConnStatus(*itConn) )
                return false;
        }
    }

    return true;
}

bool Redis::Login(redisContext* pconn, const string& passwd)
{
    if ( pconn == NULL )
        return false;

    redisReply* rsp = NULL;
    string cmd = "auth " + passwd;
    rsp = (redisReply*)redisCommand(pconn, cmd.c_str());
	
    // m_ctx->err 检测连接中断 或者 server 挂掉
    if (pconn->err != 0)
    {
        if (rsp != NULL)
        {
            freeReplyObject(rsp);
        }
        return false;
    }

    if (rsp != NULL)
        freeReplyObject(rsp);

    return true;
}

bool Redis::CheckConnStatus(RedisConnConf& conf)
{
    if (!conf.m_passwd.empty() && conf.m_conn != NULL)
    {
        if ( !Login(conf.m_conn,conf.m_passwd) )
        {
            return false;
        }
    }
    if (conf.m_conn == NULL || conf.m_conn->err != 0 )
    {
        return false;
    }
    return true;
}

void Redis::Reconnect()
{
    vector<RedisConnConf>::iterator itConn;
    vector<RedisConnConf> failedConn;
    for (itConn = m_conns.begin(); itConn != m_conns.end();)
    {
        if (itConn->m_conn == NULL)
        {
            //DEBUG_LOG("connecting to %s:%u.",itConn->first.m_ip.c_str(),itConn->first.m_port);
            itConn->m_conn = redisConnect(itConn->m_ip.c_str(), itConn->m_port);
            CheckConnStatus(*itConn);
            if ( itConn->m_conn == NULL )
            {
                 failedConn.push_back(*itConn);
                 itConn = m_conns.erase(itConn);
                 continue;
            }
        }
        else if (itConn->m_conn->err != 0)
        {
            redisFree(itConn->m_conn);
            //DEBUG_LOG("connecting to %s:%u.",itConn->first.m_ip.c_str(),itConn->first.m_port);
            itConn->m_conn = redisConnect(itConn->m_ip.c_str(), itConn->m_port);
            CheckConnStatus(*itConn);
            if ( itConn->m_conn == NULL )
            {
                 failedConn.push_back(*itConn);
                 itConn = m_conns.erase(itConn);
                 continue;
            }
        }
        ++itConn;
    }

    if ( !failedConn.empty() )
    {
        m_conns.insert(m_conns.end(),failedConn.begin(),failedConn.end());
    }
}

void Redis::SetKeyValue(const string& key, const string &value, const string &ttl)
{
    Reconnect();
    
    redisReply* rsp = NULL;
    vector<RedisConnConf>::iterator itConn;
    for (itConn = m_conns.begin(); itConn != m_conns.end(); ++itConn)
    {
        if (itConn->m_conn == NULL)
            continue;
        if ( !ttl.empty() )
        {
            rsp = (redisReply*)redisCommand(itConn->m_conn, "SET %b %b EX %s",
                key.c_str(), key.size(), value.c_str(), value.size(), ttl.c_str());
        }
        else
        {
            rsp = (redisReply*)redisCommand(itConn->m_conn, "SET %b %b",
                key.c_str(), key.size(), value.c_str(), value.size());
        }
        // m_ctx->err 检测连接中断 或者 server 挂掉
        if (itConn->m_conn->err != 0)
        {
            if (rsp != NULL)
            {
                freeReplyObject(rsp);
            }
            continue;
        }
        
        if (rsp != NULL)
            freeReplyObject(rsp);
    }
}

void Redis::HSetKeyValue(const string& hash, const string& key, const string &value, const string &ttl)
{
    Reconnect();

    redisReply* rsp = NULL;
    vector<RedisConnConf>::iterator itConn;
    for (itConn = m_conns.begin(); itConn != m_conns.end(); ++itConn)
    {
        if (itConn->m_conn == NULL)
            continue;
        rsp = (redisReply*)redisCommand(itConn->m_conn, "HSET %b %b %b",
                hash.c_str(), hash.size(), key.c_str(), key.size(), value.c_str(), value.size());
        // m_ctx->err 检测连接中断 或者 server 挂掉
        if (itConn->m_conn->err != 0)
        {
            if (rsp != NULL)
            {
                freeReplyObject(rsp);
            }
            continue;
        }
        
        if (rsp != NULL)
            freeReplyObject(rsp);
        
        if ( !ttl.empty() )
        {
            rsp = (redisReply*)redisCommand(itConn->m_conn, "EXPIRE %s %s",hash.c_str(),ttl.c_str() );
            if (rsp != NULL)
                freeReplyObject(rsp);
        }
    }
}

bool Redis::Exec(const string& cmd, redisReply **presp)
{
    Reconnect();
    bool bret = false;
    redisReply* rsp = NULL;
    *presp = NULL;
    vector<RedisConnConf>::iterator itConn;
    for (itConn = m_conns.begin(); itConn != m_conns.end(); ++itConn)
    {
        if (itConn->m_conn == NULL)
            continue;
        rsp = (redisReply*)redisCommand(itConn->m_conn, cmd.c_str());
	    
        // m_ctx->err 检测连接中断 或者 server 挂掉
        if (itConn->m_conn->err != 0)
        {
            if (rsp != NULL)
            {
                freeReplyObject(rsp);
            }
            continue;
        }

        // presp保存的不释放，由外部释放
        if (rsp != NULL)
        {
            if (REDIS_REPLY_NIL == rsp->type )
            {
                freeReplyObject(rsp);
                continue;
            }
            bret = true;
            if ( *presp == NULL )
                *presp = rsp;
            else
                freeReplyObject(rsp);
        }
    }

    return bret;
}

string Redis::HGetKeyValue(const string& hash, const string& key)
{
    Reconnect();
    
    redisReply* rsp = NULL;
    vector<RedisConnConf>::iterator itConn;
    for (itConn = m_conns.begin(); itConn != m_conns.end(); ++itConn)
    {
        if (itConn->m_conn == NULL)
            continue;
        rsp = (redisReply*)redisCommand(itConn->m_conn, "HGET %b %b",
                hash.c_str(),
                hash.size(),
                key.c_str(),
                key.size());
	    
        // m_ctx->err 检测连接中断 或者 server 挂掉
        if (itConn->m_conn->err != 0)
        {
            if (rsp != NULL)
            {
                freeReplyObject(rsp);
            }
            continue;
        }
        
        if (rsp != NULL)
        {
            if (rsp->type == REDIS_REPLY_STRING)
            {
                string tmp(rsp->str);
                freeReplyObject(rsp);
                return tmp;
            }
            freeReplyObject(rsp);
        }
    }
    return "";
}

void Redis::HGetValue(const string& table, vector<string> &result)
{
    Reconnect();

    redisReply* rsp = NULL;
    vector<RedisConnConf>::iterator itConn;
    for (itConn = m_conns.begin(); itConn != m_conns.end(); ++itConn)
    {
        if (itConn->m_conn == NULL)
            continue;
        rsp = (redisReply*)redisCommand(itConn->m_conn, "HVALS %b %b",
                table.c_str(),
                table.size());

        // m_ctx->err 检测连接中断 或者 server 挂掉
        if (itConn->m_conn->err != 0)
        {
            if (rsp != NULL)
            {
                freeReplyObject(rsp);
            }
            continue;
        }

        if (rsp != NULL)
        {
           if (rsp->type == REDIS_REPLY_ARRAY)
           {
                result.resize(rsp->elements);
                for (int i=0; i<(int)(rsp->elements); i++)
                {
                    if (REDIS_REPLY_STRING == rsp->element[i]->type)
                    {
                        result[i].assign(rsp->element[i]->str);
                    }
                }
                freeReplyObject(rsp);
                break;
            }
            freeReplyObject(rsp);
        }
    }
}

string Redis::GetKey(const string& key)
{
    Reconnect();
    
    redisReply* rsp = NULL;
    vector<RedisConnConf>::iterator itConn;
    for (itConn = m_conns.begin(); itConn != m_conns.end(); ++itConn)
    {
        if (itConn->m_conn == NULL)
            continue;
        rsp = (redisReply*)redisCommand(itConn->m_conn, "GET %b", key.c_str(), key.size());
        // m_ctx->err 检测连接中断 或者 server 挂掉
        if (itConn->m_conn->err != 0)
        {
            if (rsp != NULL)
            {
                freeReplyObject(rsp);
            }
            continue;
        }

        if (rsp != NULL)
        {
            if (rsp->type == REDIS_REPLY_STRING)
            {
                string tmp(rsp->str);
                freeReplyObject(rsp);
                return tmp;
            }
            freeReplyObject(rsp);
        }
    }

    return "";
}

void Redis::KeyPattern(const string& key_pat, vector<string>& results)
{
    Reconnect();
    
    redisReply* rsp = NULL;
    vector<RedisConnConf>::iterator itConn;
    for (itConn = m_conns.begin(); itConn != m_conns.end(); ++itConn)
    {
        if (itConn->m_conn == NULL)
            continue;
        rsp = (redisReply*)redisCommand(itConn->m_conn, "KEYS %b", key_pat.c_str(), key_pat.size());
        // m_ctx->err 检测连接中断 或者 server 挂掉
        if (itConn->m_conn->err != 0)
        {
            if (rsp != NULL)
            {
                freeReplyObject(rsp);
            }
            continue;
        }

        if (rsp != NULL)
        {
            if (rsp->type == REDIS_REPLY_ARRAY)
            {
                results.resize(rsp->elements);
                for (int i=0; i<(int)(rsp->elements); i++)
                {
                    if (REDIS_REPLY_STRING == rsp->element[i]->type)
                    {
                        results[i] = rsp->element[i]->str;
                    }
                }
                freeReplyObject(rsp);
                break;
            }
            freeReplyObject(rsp);
        }
    }
}


long long Redis::GetInt(const string& key)
{
    Reconnect();

    long long result = 0;
    redisReply* rsp = NULL;
    vector<RedisConnConf>::iterator itConn;

    for (itConn = m_conns.begin(); itConn != m_conns.end(); ++itConn)
    {
        if (itConn->m_conn == NULL)
            continue;
        rsp = (redisReply*)redisCommand(itConn->m_conn,"GET %b", key.c_str(), key.size());
        
        if (itConn->m_conn->err != 0)
        {
            if (rsp != NULL)
            {
                freeReplyObject(rsp);
            }
            continue;
        }

        if (rsp != NULL)
        {
            if (rsp->type == REDIS_REPLY_INTEGER)
            {
                result = rsp->integer;
                freeReplyObject(rsp);
                return result;
            }
            else if (rsp->type == REDIS_REPLY_NIL)
            {
                result = 0;
                freeReplyObject(rsp);
                continue;
            }
            else if (rsp->type == REDIS_REPLY_STRING)
            {
                result = atoi(rsp->str);
                freeReplyObject(rsp);
                return result;
            }
            freeReplyObject(rsp);
        }
    }
    return result;
}



